package nullby.common.utils;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.net.URLEncoder;

import android.content.Context;
import android.os.Bundle;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;

/**
 * Provides some utile for url addres
 * 
 * @author Sergey Bandysik (bandysik[at]gmail[dot]com)
 */
public final class UrlUtil {

	private static final String TAG = UrlUtil.class.getSimpleName();

	@SuppressWarnings("deprecation")
	public static String encodeUrl(final Bundle parameters) {
		if (parameters == null) {
			return "";
		}

		StringBuilder sb = new StringBuilder();
		boolean first = true;
		for (String key : parameters.keySet()) {
			Object parameter = parameters.get(key);
			if (!(parameter instanceof String)) {
				continue;
			}

			if (first) {
				first = false;
			} else {
				sb.append("&");
			}
			sb.append(URLEncoder.encode(key) + "="
					+ URLEncoder.encode(parameters.getString(key)));
		}
		return sb.toString();
	}

	@SuppressWarnings("deprecation")
	public static Bundle decodeUrl(final String s) {
		Bundle params = new Bundle();
		if (s != null) {
			String array[] = s.split("&");
			for (String parameter : array) {
				String v[] = parameter.split("=");
				if (v.length == 2) {
					params.putString(URLDecoder.decode(v[0]),
							URLDecoder.decode(v[1]));
				}
			}
		}
		return params;
	}

	/**
	 * Connect to an HTTP URL and return the response as a string.
	 * 
	 * Note that the HTTP method override is used on non-GET requests. (i.e.
	 * requests are made as "POST" with method specified in the body).
	 * 
	 * @param url
	 *            - the resource to open: must be a welformed URL
	 * @param method
	 *            - the HTTP method to use ("GET", "POST", etc.)
	 * @param params
	 *            - the query parameter for the URL (e.g. access_token=foo)
	 * @return the URL contents as a String
	 * @throws MalformedURLException
	 *             - if the URL format is invalid
	 * @throws IOException
	 *             - if a network problem occurs
	 */
	@SuppressWarnings("deprecation")
	public static String openUrl(String url, final String method,
			final Bundle params) throws MalformedURLException, IOException {
		// random string as boundary for multi-part http post
		String strBoundary = "3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f";
		String endLine = "\r\n";

		OutputStream os;

		if (method.equals("GET")) {
			url = url + "?" + encodeUrl(params);
		}

		CustomLog.logD(TAG, method + " URL: " + url);
		HttpURLConnection conn = (HttpURLConnection) new URL(url)
				.openConnection();
		conn.setRequestProperty("User-Agent", System.getProperties()
				.getProperty("http.agent"));
		if (!method.equals("GET")) {
			Bundle dataparams = new Bundle();
			for (String key : params.keySet()) {
				Object parameter = params.get(key);
				if (parameter instanceof byte[]) {
					dataparams.putByteArray(key, (byte[]) parameter);
				}
			}

			// use method override
			if (!params.containsKey("method")) {
				params.putString("method", method);
			}

			if (params.containsKey("access_token")) {
				String decoded_token = URLDecoder.decode(params
						.getString("access_token"));
				params.putString("access_token", decoded_token);
			}

			conn.setRequestMethod("POST");
			conn.setRequestProperty("Content-Type",
					"multipart/form-data;boundary=" + strBoundary);
			conn.setDoOutput(true);
			conn.setDoInput(true);
			conn.setRequestProperty("Connection", "Keep-Alive");
			conn.connect();
			os = new BufferedOutputStream(conn.getOutputStream());

			os.write(("--" + strBoundary + endLine).getBytes());
			os.write((encodePostBody(params, strBoundary)).getBytes());
			os.write((endLine + "--" + strBoundary + endLine).getBytes());

			if (!dataparams.isEmpty()) {

				for (String key : dataparams.keySet()) {
					os.write(("Content-Disposition: form-data; filename=\""
							+ key + "\"" + endLine).getBytes());
					os.write(("Content-Type: content/unknown" + endLine + endLine)
							.getBytes());
					os.write(dataparams.getByteArray(key));
					os.write((endLine + "--" + strBoundary + endLine)
							.getBytes());

				}
			}
			os.flush();
		}

		String response = "";
		try {
			response = read(conn.getInputStream());
		} catch (FileNotFoundException e) {
			// Error Stream contains JSON that we can parse to a FB error
			response = read(conn.getErrorStream());
		}
		return response;
	}

	private static String read(InputStream in) throws IOException {
		final StringBuilder sb = new StringBuilder();
		try {
			BufferedReader r = new BufferedReader(new InputStreamReader(in),
					1000);
			for (String line = r.readLine(); line != null; line = r.readLine()) {
				sb.append(line);
			}
		} catch (Exception e) {
		} finally {
			in.close();
		}
		return sb.toString();
	}

	public static void clearCookies(Context context) {
		// Edge case: an illegal state exception is thrown if an instance of
		// CookieSyncManager has not be created. CookieSyncManager is normally
		// created by a WebKit view, but this might happen if you start the
		// app, restore saved state, and click logout before running a UI
		// dialog in a WebView -- in which case the app crashes
		@SuppressWarnings("unused")
		CookieSyncManager cookieSyncMngr = CookieSyncManager
				.createInstance(context);
		CookieManager cookieManager = CookieManager.getInstance();
		cookieManager.removeAllCookie();
	}

	/**
	 * Generate the multi-part post body providing the parameters and boundary
	 * string
	 * 
	 * @param parameters
	 *            the parameters need to be posted
	 * @param boundary
	 *            the random string as boundary
	 * @return a string of the post body
	 */
	public static String encodePostBody(final Bundle parameters,
			final String boundary) {
		if (parameters == null) {
			return "";
		}
		StringBuilder sb = new StringBuilder();

		for (String key : parameters.keySet()) {
			Object parameter = parameters.get(key);
			if (!(parameter instanceof String)) {
				continue;
			}

			sb.append("Content-Disposition: form-data; name=\"" + key
					+ "\"\r\n\r\n" + (String) parameter);
			sb.append("\r\n" + "--" + boundary + "\r\n");
		}

		return sb.toString();
	}
}
